package cn.com.redenvelope.redis;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import cn.com.redenvelope.RedEnvelope;
import cn.com.redenvelope.RedEnvelopeSnatchListener;
import cn.com.redenvelope.RedEnvelopeSnatcher;
import cn.com.redenvelope.SnatchResult;
import cn.com.redis.RedisDistributionConnectionTool;
import redis.clients.jedis.ShardedJedis;

public class RedisRedEnvelopeSnatcher implements RedEnvelopeSnatcher {
	private static Logger _log = LogManager.getLogger(RedisRedEnvelopeSnatcher.class.getName());

	private RedisDistributionConnectionTool redisTool = null;
	private ShardedJedis client = null;

	private String envelopeKey = null;
	private String envelopeSnatchedKey = null;

	private int memberId = 0;
	private boolean memberSnatched = false;

	public RedisRedEnvelopeSnatcher() {
		this.redisTool = new RedisDistributionConnectionTool();
		this.client = this.redisTool.getRedisClient();
	}

	private String getEnvelopeSnatchedKey() {
		return envelopeSnatchedKey;
	}

	public String makeEnvelopeSnatchedKey(int envelopeId) {
		String envelopeSnatchedKey = "evm:" + envelopeId;
		this.envelopeSnatchedKey = envelopeSnatchedKey;
		return envelopeSnatchedKey;
	}

	private String makeEnvelopeKey(int envelopeId) {
		String envelopeKey = "ev:" + envelopeId;
		this.envelopeKey = envelopeKey;
		return envelopeKey;
	}

	public String getEnvelopeKey() {
		return this.envelopeKey;
	}

	@Override
	public boolean hasMoreEnvelope() {
		try {
			String key = this.getEnvelopeKey();

			if (this.client.exists(key)) {
				if (this.client.llen(key) > 0) {
					return true;
				}
			}
		} catch (Exception ex) {
			ex.printStackTrace();
			_log.error("Redis execution failure. Message:" + ex.getMessage());
			this.redisTool.returnBrokenClientResource(this.client);
		}
		return false;
	}

	@Override
	public SnatchResult snatch(RedEnvelopeSnatchListener listener) {
		SnatchResult result = SnatchResult.FAILURE;
		try {
			synchronized (RedisRedEnvelopeSnatcher.class) {
				if (!this.memberSnatched) {

					if (this.client.exists(this.getEnvelopeKey())) {
						String nextMoney = this.client.lpop(this.getEnvelopeKey());
						if (null != nextMoney) {
							this.client.rpush(this.getEnvelopeSnatchedKey(), this.memberId + ":" + nextMoney);
							result = SnatchResult.SUCCESS;
							listener.onSnatchSuccess(this.getEnvelope());
						}
						if (!this.client.exists(this.getEnvelopeKey())) {
							listener.onEnvelopeComplete(this.getEnvelope());
						}
					} else {
						if (this.client.llen(this.getEnvelopeSnatchedKey()) > 0) {
							result = SnatchResult.ENVELOPE_COMPLETE;
						} else {
							result = SnatchResult.ENVELOPE_NOT_AVAILABLE;
							listener.onEnvelopeInvalid(this.getEnvelope());
						}
					}

				} else {
					result = SnatchResult.MEMBER_HAS_SNATCHED;
				}
			}
		} catch (Exception ex) {
			result = SnatchResult.FAILURE;
			ex.printStackTrace();
			_log.error("Redis execution failure. Message:" + ex.getMessage());
			this.redisTool.returnBrokenClientResource(this.client);
		}
		return result;
	}

	private RedEnvelope getEnvelope() {
		RedEnvelope redEnvelope = new RedEnvelope();

		int totalCount = 0;
		int totalMoney = 0;
		Map<Integer, Integer> snatchedMembers = new HashMap<Integer, Integer>();

		String key = this.getEnvelopeSnatchedKey();

		if (this.client.exists(key)) {
			long len = this.client.llen(key);
			List<String> snatches = this.client.lrange(key, 0, len - 1);

			for (String snatch : snatches) {
				String[] items = snatch.split(":");
				int memberId = Integer.valueOf(items[0]);
				int money = Integer.valueOf(items[1]);
				snatchedMembers.put(memberId, money);
				totalCount++;
				totalMoney += money;
			}
			redEnvelope.setSnatchers(snatchedMembers);
		}

		key = this.getEnvelopeKey();

		if (this.client.exists(key)) {
			long len = this.client.llen(key);
			List<String> moneys = this.client.lrange(key, 0, len - 1);
			for (String money : moneys) {
				totalCount++;
				totalMoney += Integer.valueOf(money);
			}
		}

		redEnvelope.setTotalMoney(totalMoney);
		redEnvelope.setCount(totalCount);

		return redEnvelope;
	}

	@Override
	public SnatchResult initSnatcher(int envelopeId, int memberId) {
		SnatchResult result = SnatchResult.FAILURE;

		String key = this.makeEnvelopeKey(envelopeId);

		this.makeEnvelopeSnatchedKey(envelopeId);

		try {
			if (this.client.exists(key)) {
				this.memberId = memberId;
				this.memberSnatched = this.isMemberHasSnatched(memberId);
				if (this.memberSnatched) {
					result = SnatchResult.MEMBER_HAS_SNATCHED;
				} else {
					result = SnatchResult.SUCCESS;
				}
			} else {
				if (this.client.llen(this.getEnvelopeSnatchedKey()) > 0) {
					result = SnatchResult.ENVELOPE_COMPLETE;
				} else {
					result = SnatchResult.ENVELOPE_NOT_AVAILABLE;
				}
			}
		} catch (Exception ex) {
			result = SnatchResult.FAILURE;
			ex.printStackTrace();
			_log.error("Redis execution failure. Message:" + ex.getMessage());
			this.redisTool.returnBrokenClientResource(this.client);
		} finally {
			this.redisTool.returnClientResource(this.client);
		}

		return result;
	}

	private boolean isMemberHasSnatched(int memberId) {
		String key = this.getEnvelopeSnatchedKey();
		if (this.client.exists(key)) {
			long len = this.client.llen(key);
			List<String> snatches = this.client.lrange(key, 0, len - 1);
			for (String snatch : snatches) {
				if (Integer.valueOf(snatch.split(":")[0]) == memberId) {
					return true;
				}
			}
		}

		return false;
	}

	protected void finalize() {
		try {
			this.redisTool.returnClientResource(this.client);
			super.finalize();
		} catch (Throwable e) {
			e.printStackTrace();
			_log.error("Release Redis connection pool failure. Message:" + e.getMessage());
		}
	}
}
